/*
 * Copyright (c) 2021  Haowei Wen <yushijinhun@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#include <opencv2/core/mat.hpp>
#include <thread>

namespace apriltag {

enum class Family {
  tag36h11,
  tag25h9,
  tag16h5,
  tagCircle21h7,
  tagCircle49h12,
  tagStandard41h12,
  tagStandard52h13,
  tagCustom48h12,
};

class Config {
public:
  // Tag family to use
  Family family = Family::tag36h11;

  // Use this many CPU threads
  int nthreads = std::thread::hardware_concurrency();

  // Decimate input image by this factor
  double quad_decimate = 1.0;

  // Apply low-pass blur to input
  double quad_sigma = 0.0;

  // Spend more time trying to align edges of tags
  bool refine_edges = true;
};

class Detection {
public:
  // Tag family
  Family family;

  // The decoded ID of the tag
  int id;

  // How many error bits were corrected? Note: accepting large numbers of
  // corrected errors leads to greatly increased false positive rates.
  // NOTE: As of this implementation, the detector cannot detect tags with
  // a hamming distance greater than 2.
  int hamming;

  // A measure of the quality of the binary decoding process: the
  // average difference between the intensity of a data bit versus
  // the decision threshold. Higher numbers roughly indicate better
  // decodes. This is a reasonable measure of detection accuracy
  // only for very small tags-- not effective for larger tags (where
  // we could have sampled anywhere within a bit cell and still
  // gotten a good detection.)
  float decision_margin;

  // The 3x3 homography matrix describing the projection from an
  // "ideal" tag (with corners at (-1,1), (1,1), (1,-1), and (-1,
  // -1)) to pixels in the image.
  cv::Matx33d homography_matrix;

  // The center of the detection in image pixel coordinates.
  cv::Point2d center;

  // The corners of the tag in image pixel coordinates. These always
  // wrap counter-clock wise around the tag.
  std::array<cv::Point2d, 4> corners;
};

class Detector {
public:
  Detector(const Config &cfg = Config());
  ~Detector();

  std::vector<Detection> detect(const cv::Mat &grayscale_image);

private:
  class Impl;
  std::unique_ptr<Impl> impl;
};

}; // namespace apriltag
